//
// Created by konglinglong on 2020/9/4.
//

#include <math.h>
#include "../../include/utils/misc.h"

uint64_t gCspCpuFreqHz  = 1;
uint64_t gCspCpuFreqKHz = 1;
uint64_t gCspCpuFreqMHz = 1;
double   gCspCpuFreqGHz = 1.0;

void CSP_InitCpuFreqVar(void)
{
    uint64_t start = CSP_rdtsc();
    sleep(1);
    gCspCpuFreqHz = CSP_rdtsc() - start;
    gCspCpuFreqKHz = gCspCpuFreqHz / 1000;
    gCspCpuFreqMHz = gCspCpuFreqHz / 1000000;
    gCspCpuFreqGHz = (double)gCspCpuFreqHz / 1000000000;

    printf("CPU Freq [%lu, %lu, %lu, %f] \n",
        gCspCpuFreqHz, gCspCpuFreqKHz, gCspCpuFreqMHz, gCspCpuFreqGHz);
}

int32_t CSP_Bin2Hex(const char* src, uint32_t srcLen, char* dst, uint32_t dstLen)
{
    uint8_t value;
    uint32_t loop;
    uint32_t idx;
    uint32_t outCharNum;

    static char bin2Hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    if (dstLen < 3)
    {
        return 0;
    }

    outCharNum = (dstLen < srcLen * 3 + 3) ? (dstLen - 3) / 3 : srcLen;
    for (loop = 0; loop < outCharNum; loop++)
    {
        idx = loop * 3;
        value = src[loop];
        dst[idx] = bin2Hex[value >> 4];
        dst[idx + 1] = bin2Hex[value & 0xf];
        dst[idx + 2] = (15 == loop % 16) ? '\n' : ' ';
    }
    dst[loop * 3] = (outCharNum == srcLen) ? '.' : '!';
    dst[loop * 3 + 1] = '\n';
    dst[loop * 3 + 2] = '\0';

    return (int32_t)(loop * 3 + 2);
}

void CSP_DelayStrictUs(uint64_t *timeStamp, uint64_t delayUs)
{
    uint64_t nowTick;
    uint64_t delayTick;
    uint64_t elapsed;
    struct timespec ts;

    clock_gettime(CLOCK_MONOTONIC, &ts);
    nowTick = (uint64_t)ts.tv_sec * 1000 * 1000 + ts.tv_nsec / 1000;
    elapsed = nowTick - *timeStamp;
    delayTick = elapsed > delayUs ? 0 : (delayUs - elapsed);
    *timeStamp = nowTick;
    usleep(delayTick);
}

int32_t CSP_ArrivalDelayTimeUs(uint64_t *lastTimeStamp, uint64_t delayUs)
{
    uint64_t nowTick;
    struct timespec ts;

    clock_gettime(CLOCK_MONOTONIC, &ts);
    nowTick = (uint64_t)ts.tv_sec * 1000 * 1000 + ts.tv_nsec / 1000;
    if ((nowTick - (*lastTimeStamp)) >= delayUs)
    {
        *lastTimeStamp = nowTick;
        return TRUE;
    }
    return FALSE;
}

/* 对数Log2x的映射表，取上整 */
const uint8_t gLog2xTable[] =
{
    /* log2x */
    0, /* x = 0无效 */
    0, /* x = 1 */
    1, /* x = 2 */
    2, 2, /* x = 4 */
    3, 3, 3, 3, /* x = 8 */
    4, 4, 4, 4, 4, 4, 4, 4, /* x = 16 */
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, /* x = 32 */
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, /* x = 64 */
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* x = 128 */
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, /* x = 256 */
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9  /* x = 512 */
};
STATIC_ASSERT(ARRAY_SIZE(gLog2xTable) == LOG2X_TABLE_SIZE);

uint32_t CSP_FastLog2Uint(uint64_t n)
{
#define CSP_LOG2_SHFIT(k) if (n >= (UINT64_C(1) << k)) { i += k; n >>= k; }

    uint32_t i = 0;
    CSP_LOG2_SHFIT(32);
    CSP_LOG2_SHFIT(16);
    CSP_LOG2_SHFIT(8);
    CSP_LOG2_SHFIT(4);
    CSP_LOG2_SHFIT(2);
    CSP_LOG2_SHFIT(1);
    return i;

#undef CSP_LOG2_SHFIT
}

uint32_t CSP_FastLog10Uint(uint64_t n)
{
#define CSP_LOG10_SHFIT(k, m) if (n >= UINT64_C(m)) { i += k; n /= UINT64_C(m); }

    uint32_t i = 0;
    CSP_LOG10_SHFIT(16,10000000000000000);
    CSP_LOG10_SHFIT(8,100000000);
    CSP_LOG10_SHFIT(4,10000);
    CSP_LOG10_SHFIT(2,100);
    CSP_LOG10_SHFIT(1,10);
    return i;

#undef CSP_LOG10_SHFIT
}

double CSP_FastLog2Float(double x)
{
#define CSP_LOG2_FLOAT_PRECISION  10
#define CSP_LOG2_FLOAT_TABLE_SIZE (1 << CSP_LOG2_FLOAT_PRECISION)

    uint32_t loop;
    static uint32_t log2FloatTableInit = 0;
    static double log2FloatTable[CSP_LOG2_FLOAT_TABLE_SIZE];

    if (unlikely(!log2FloatTableInit))
    {
        for (loop = 0; loop < CSP_LOG2_FLOAT_TABLE_SIZE; loop++)
        {
            log2FloatTable[loop] = log2(1 + (double)loop / CSP_LOG2_FLOAT_TABLE_SIZE);
        }
        log2FloatTableInit = 1;
    }

    long long t = *(long long*)&x;
    int exp = (t >> 52) - 0x3ff;
    int mantissa = (t >> (52 - CSP_LOG2_FLOAT_PRECISION)) & (CSP_LOG2_FLOAT_TABLE_SIZE - 1);
    return exp + log2FloatTable[mantissa];
}

double CSP_FastLog10Float(double x)
{
    return 0.301029996 * CSP_FastLog2Float(x);
}

